#!/usr/bin/env python
"""
Acorn-SDR ('A Collection of Radio Nodes')
(c) Bob Cowdery (G3UKB) 2009

File: dashboard_service.py
Date: July 2009

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

The author can be reached by email at

bob@g3ukb.co.uk

================================================================================
Module Description:

The dashboard. This will query the profile to be used, select the profile and
load the required processes. It provides a way to establish the initial window
layout. Profiles can be started and stopped via the dashboard.

"""
#-------------------------------------------------------------------------------
# System imports
import sys
import os
from os import kill
import signal
from time import sleep
import subprocess
import Ice, IceStorm

# Application imports
import gtk_dashboard_gui as gui
import ice_helpers
import utils

# Import the Ice interface definitions
import AcornPersist
import AcornLifeCycle

#===============================================================================
# Ice life-cycle events for the GUI
class LifeCycleI(AcornLifeCycle.LifeCycle):
    
    #---------------------------------------------------------------------------   
    def __init__(self, service):
        self.service = service
      
    #---------------------------------------------------------------------------
    def start(self, current=None):
        pass
     
    #---------------------------------------------------------------------------   
    def stop(self, current=None):
        pass
    
    #---------------------------------------------------------------------------    
    def terminate(self, current=None):
        self.service.terminate()
        
#===============================================================================
# The main class of the dashboard GUI application
class AppDashboardGui(Ice.Application):
  
    #---------------------------------------------------------------------------
    def __init__(self, home):
        self.HOME = home
        self.close = False
        self.not_me = False
        self.i_started_persist = False
        self.start_jack = False
        self.proc_list = []
            
    #---------------------------------------------------------------------------
    # Run the app
    def run(self, args):
        self.args = args
        
        #-----------------------------------------------------------------------
        # Event names used here
        subscribe_to_lc = 'life_cycle_events'
        publish_to_lc = 'life_cycle_events'
        
        #-----------------------------------------------------------------------
        # Try to connect to the persist service as it may be running on another machine
        self.start_persist()
        
        #-----------------------------------------------------------------------
        # This is the same for both publishers and subscribers
        # Get the IceStorm topic manager interface
        manager = ice_helpers.get_topic_manager('Dashboard', self.communicator)
    
        # Retrieve the subscriber topic
        life_cycle_topic = ice_helpers.get_topic('Dashboard', manager, subscribe_to_lc)
        # Retrieve the publisher topics
        publish_topic_life_cycle = ice_helpers.get_topic('Dashboard', manager, publish_to_lc)
        
        #-----------------------------------------------------------------------
        # Publisher side
        # Get the topic's publisher object, and create a hw proxy with
        # the mode specified as an argument of this application.
        #
        publisher_life_cycle = publish_topic_life_cycle.getPublisher();
        publisher_life_cycle = publisher_life_cycle.ice_oneway();        
        self.lc_pub = AcornLifeCycle.LifeCyclePrx.uncheckedCast(publisher_life_cycle)
        
        #-----------------------------------------------------------------------
        # Subscriber side
        # Create an adapter for the subscribers   
        adapter = self.communicator().createObjectAdapter("LifeCycle.Subscriber")
        subId = Ice.Identity()
        subId.name = Ice.generateUUID()        
        # Add the update interface
        subscriber = adapter.add(LifeCycleI(self), subId)
        # Subscribe to the topic - set up proxy
        ice_helpers.subscribe('Dashboard', life_cycle_topic, subscriber, id)
        # Now we can receive callbacks   
        adapter.activate()
        
        #-----------------------------------------------------------------------
        # Initialise the GUI
        gui_impl = gui.DashboardGui(self.glade_path, self.persist, self.lc_pub, self.start_processes, self.stop_processes, self.quit)
        
        #-----------------------------------------------------------------------
        print 'Acorn-SDR (Dashboard) running..'
        
        # Start the GUI
        gui_impl.main()
        
        #-----------------------------------------------------------------------
        # Loop until closedown
        while not self.close:
            # Idle loop for doing any other work
            sleep(1)
    
    #--------------------------------------------------------------------------- 
    def start_persist(self):
        #-----------------------------------------------------------------------
        # Try to connect to the persist service as it may be running on another machine
        if self.i_started_persist:
            start_persist = True
        else:
            try:
                print 'Trying to connect to a persistence service...'
                persist = ice_helpers.get_interface('Dashboard', self.communicator, AcornPersist.PersistPrx, 'Persist.Proxy')
                print 'Found a running service.'
                self.i_started_persist = False
                start_persist = False
            except:
                # Nope
                print 'No service found so starting a persistence service.'
                self.i_started_persist = True
                start_persist = True
        
        if start_persist:
            # In order to start the persist process we need to have some things in the directory tree fixed.
            # 1. 'ACORN_HOME/persistence' is where the persist scripts live
            # 2. The service is called persist_service.py
            # 2. ../conf/ice is the relative path from ACORN_HOME where the ice config lives
            # 3. The main config file is called acorn_ice.config
            # If persist is not running its a fair assumption that IceStorm isn't running either
            # We need to be in the right directory to start IceStorm
            os.chdir(HOME + '/../conf/ice')
            p = subprocess.Popen(['icebox', '--Ice.Config=config.icebox'])
            self.proc_list.append(p)
            sleep(1)
            os.chdir(HOME)
            p = subprocess.Popen(['python', self.HOME + '/persistance/persist_service.py', self.args[1]])
            self.proc_list.append(p)
            
            #-----------------------------------------------------------------------
            # Get the persist interface
            self.persist = ice_helpers.get_interface('Dashboard', self.communicator, AcornPersist.PersistPrx, 'Persist.Proxy')
        # Get essential config data
        self.glade_path = utils.persist_get(self.persist, 'Profile', 'path.glade')
        
    #---------------------------------------------------------------------------
    def start_processes(self):        
        # A profile has now been set. See what we need to start.
        # Does Jack need starting
        self.start_jack = utils.persist_get(self.persist, 'Profile', 'jack.load')
        if self.start_jack:
            fixed = utils.persist_get(self.persist, 'Profile', 'audio.outputfixed')
            periods = utils.persist_get(self.persist, 'Profile', 'jack.periods')
            if fixed:
                # These are the fixed params for jack
                rate = 48000
                frames = 512
            else:
                # Take it from the config
                rate = utils.persist_get(self.persist, 'Profile', 'audio.rate')
                frames = utils.persist_get(self.persist, 'Profile', 'audio.frames')
            p = subprocess.Popen(['jackd', '-dalsa', '-dhw:0', '-r' + str(rate), '-p' + str(frames), '-n' + str(periods)])
            self.proc_list.append(p)
        # Now start the application processes    
        processes = utils.persist_get(self.persist, 'Profile', 'processes')
        process_list = processes.rsplit(',')
        for process in process_list:
            # Start each process
            path = utils.persist_get(self.persist, 'Profile', 'path.' + process)
            p = subprocess.Popen(['python', self.HOME + path, self.args[1]])
            self.proc_list.append(p)
        
    #---------------------------------------------------------------------------
    def stop_processes(self):
        self.not_me = True
        self.lc_pub.terminate()
        sleep(1)
        if self.start_jack:
            subprocess.Popen(['killall', 'jackd'])
        subprocess.Popen(['killall', 'icebox'])
        self.wait(self.proc_list)
        self.start_persist()
        
    #---------------------------------------------------------------------------
    def quit(self):
        # Tell everyone to go away
        self.lc_pub.terminate()
        sleep(1)
        if self.start_jack:
            subprocess.Popen(['killall', 'jackd'])
        subprocess.Popen(['killall', 'icebox'])
        self.wait(self.proc_list)
            
    #---------------------------------------------------------------------------
    # Terminate
    def terminate(self):
        # Quit is we are actually closing down
        if self.not_me:
            self.not_me = False
        else:
            self.close = True
        
    #---------------------------------------------------------------------------           
    def wait(self, procs):
        # We must wait or processes may get zombified
        for proc in procs:
            proc.wait()
        
#===============================================================================
#===============================================================================
# Start from the command line
if __name__ == "__main__":
   
    # Get the persist service, can't do anything without it
    # First get the home directory for this system or fragment
    try:
        HOME = os.environ['ACORN_HOME']
    except:
        # Can't do much without that
        print 'Please set environment variable ACORN_HOME to point to the parent Acorn directory!'
        exit
        
    # Set our current directory
    os.chdir(HOME)
    
    app = AppDashboardGui(HOME)
    app.main(sys.argv, sys.argv[1])
  
    # Announce we are going 
    print 'Acorn-SDR (Dashboard) closing..'
  